package com.accesshollywood.components;

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.LinkedHashMap;

import javax.swing.AbstractCellEditor;
import javax.swing.BorderFactory;
import javax.swing.JList;
import javax.swing.JTextField;
import javax.swing.ListCellRenderer;
import javax.swing.ListSelectionModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import santhosh.DefaultMutableListModel;
import santhosh.JListMutable;
import santhosh.ListCellEditor;
import santhosh.MutableListModel;

import com.accesshollywood.grfxOrdering.OrderMainGUI;
import com.accesshollywood.indexables.Indexed;
import com.accesshollywood.indexables.User;

public class EditableListWithPopup<T extends Indexed> extends JListMutable implements HasPopupMenu {
	public BasicPopupMenu							popup				= new BasicPopupMenu(this);
	private T										oldEntry			= null;
	private LinkedHashMap<String, ActionListener>	popupAddlActions	= new LinkedHashMap<String, ActionListener>();
	protected Class									refreshMe			= OrderMainGUI.class;
	protected EditableListModel						model				= new EditableListModel();
	private int										fontSize			= 11;

	private final Class<T>							type;

	public EditableListWithPopup(int fontSize, Class<T> t) {
		super();
		this.type = t;
		init(fontSize);
		setupPopup();
	}

	public EditableListWithPopup(EditableListModel dataModel, int fontSize, Class<T> t) {
		super(dataModel);
		this.type = t;
		model = dataModel;
		init(fontSize);
		setupPopup();
	}
	
	public Class setType(Type t) {
		/*
		try {
			Field f = this.getClass().getDeclaredField("oldEntry");
			Class c = f.getClass();
			Type gtype = f.getGenericType();
			ParameterizedType ptype = (ParameterizedType)gtype;
			Type rtype = ptype.getRawType();
			Type[] targs = ptype.getActualTypeArguments();
			
			System.out.println(c.getName()+"   "+gtype.toString()+"   "+ptype.toString()+"   "+rtype.getClass().getName());
			for(Type t : targs){
				System.out.println(t.getClass().getName());
			}
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		}
		
		return (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
		*/
		return null;
	}

	public void init(int fontSize) {
		this.fontSize = fontSize;
		this.setListCellEditor(new ListEditRenderer());
		this.setCellRenderer(new ListEditRenderer());
		ListSelectionModel listSelectionModel = this.getSelectionModel();
		/*
		 * listSelectionModel.addListSelectionListener(new ListSelectionListener(){
		 * @Override
		 * public void valueChanged(ListSelectionEvent e) {
		 * ListSelectionModel lsm = (ListSelectionModel)e.getSource();
		 * int firstIndex = e.getFirstIndex();
		 * int lastIndex = e.getLastIndex();
		 * boolean isAdjusting = e.getValueIsAdjusting();
		 * System.out.println("Event for indexes " + firstIndex + " - " + lastIndex+ "; isAdjusting is " + isAdjusting + "; selected indexes:");
		 * if (lsm.isSelectionEmpty()) {
		 * System.out.println(" <none>");
		 * } else {
		 * // Find out which indexes are selected.
		 * int minIndex = lsm.getMinSelectionIndex();
		 * int maxIndex = lsm.getMaxSelectionIndex();
		 * for (int i = minIndex; i <= maxIndex; i++) {
		 * if (lsm.isSelectedIndex(i)) {
		 * System.out.println(" " + i);
		 * }
		 * }
		 * }
		 * System.out.println("------------------");
		 * }
		 * });
		 */
	}

	public void setupPopup() {
		popup.addMenuItem("Add", new AddActionListener());
		// popup.addMenuItem("Delete", new DeleteActionListener());
	}

	public synchronized Class getRefreshMe() {
		return refreshMe;
	}

	public synchronized void setRefreshMe(Class refreshMe) {
		this.refreshMe = refreshMe;
	}

	public void refresh(boolean fromSQL) {
		try {
			Method refreshMethod = refreshMe.getDeclaredMethod("refresh", boolean.class);
			refreshMethod.invoke(refreshMe, fromSQL);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public synchronized T getOldEntry() {
		return oldEntry;
	}

	public class ListEditRenderer extends AbstractCellEditor implements ListCellRenderer, ListCellEditor, ChangeListener {
		private JTextField	tf	= new JTextField();
		private T			oldEntry;

		public ListEditRenderer() {
			super();
			tf.setBorder(null);
			tf.setFont(new Font(tf.getFont().getName(), tf.getFont().getStyle(), fontSize));
		}

		public void stateChanged(ChangeEvent e) {
			fireEditingStopped();
		}

		public boolean isCellEditable(EventObject anEvent) {
			if (anEvent instanceof MouseEvent) {
				return ((MouseEvent) anEvent).getClickCount() >= 2;
			}
			return true;
		}

		protected void fireEditingStopped() {
			super.fireEditingStopped();
			tf.setBorder(null);
			// tf.removeChangeListener(this);
		}

		protected void fireEditingCanceled() {
			super.fireEditingCanceled();
			tf.setBorder(null);
			// tf.removeChangeListener(this);
		}

		@SuppressWarnings("unchecked")
		public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
			tf.setText(((T) value).toString());
			if (isSelected) {
				tf.setBackground(list.getSelectionBackground());
				tf.setForeground(list.getSelectionForeground());
			} else {
				tf.setBackground(list.getBackground());
				tf.setForeground(list.getForeground());
			}
			return tf;
		}

		@SuppressWarnings("unchecked")
		public T getCellEditorValue() {
			String value = tf.getText();
			if (!value.equals(oldEntry.toString())) {
				oldEntry.fromString(value);
			}
			return oldEntry;
		}

		public Component getListCellEditorComponent(JList list, Object value, boolean isSelected, int index) {
			oldEntry = (T) value;
			tf.setText(((Indexed) oldEntry).toString());
			tf.setBorder(BorderFactory.createLineBorder(Color.BLACK));
			return tf;
		}

	}

	// -----------------------------------------------------------------

	public void setModel(EditableListModel model) {
		super.setModel(model);
		this.model = model;
	}

	public EditableListModel getModel() {
		return model;
	}

	public void setValueAt(Object value, int index) {
		((Indexed) value).toSQL();
		((MutableListModel) getModel()).setValueAt(value, index);
		T obj = (T) ((MutableListModel) getModel()).getElementAt(index);
		obj.fromSQL();
	}

	public boolean isCellEditable(int index) {
		if (getModel() instanceof MutableListModel)
			return ((MutableListModel) getModel()).isCellEditable(index);
		return false;
	}

	public synchronized int getFontSize() {
		return fontSize;
	}

	public synchronized void setFontSize(int fontSize) {
		this.fontSize = fontSize;
	}

	// -----------------------------------------------------------------
	public class AddActionListener implements ActionListener {
		@SuppressWarnings("unchecked")
		@Override
		public void actionPerformed(ActionEvent arg0) {
			Indexed newEntry;
			try {
				newEntry = (T) type.newInstance();
				if (newEntry != null) {
					int id = newEntry.toSQL();
					((EditableListModel) getModel()).addElement(newEntry);
				}
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			/*
			 * ViewFilterPreset newPreset = new ViewFilterPreset();
			 * newPreset.setOwner(Users.currentUser);
			 * newPreset.setName("New Preset...");
			 * if (newPreset.getName() != null && newPreset.getName().length() > 0) {
			 * Collections.sort(ViewFiltersMasterPanel.selectedFilters, new Comparator() {
			 * public int compare(Object o1, Object o2) {
			 * ViewFilter v1 = (ViewFilter) o1;
			 * ViewFilter v2 = (ViewFilter) o2;
			 * return v1.getGroupName().compareToIgnoreCase(v2.getGroupName());
			 * }
			 * });
			 * if (ViewFiltersMasterPanel.selectedFilters.size() > 0) {
			 * for (int i = 0; i < ViewFiltersMasterPanel.selectedFilters.size(); i++) {
			 * ViewFilter currFilter = (ViewFilter) ViewFiltersMasterPanel.selectedFilters.get(i);
			 * //newPreset.getCheckedFilters().put(currFilter.hashCode(), currFilter);
			 * newPreset.getViewFilterList().add(currFilter);
			 * }
			 * }
			 * int id = newPreset.toSQL();
			 * // ViewFilterPreset indexedViewFilterPreset = new ViewFilterPreset();
			 * // indexedViewFilterPreset.setId(id);
			 * // indexedViewFilterPreset.setCore(newPreset, false);
			 * ((EditableListModel) getModel()).addElement(newPreset);
			 * }
			 */
			refresh(true);
		}
	}

	public class EditActionListener implements ActionListener {

		private Component	dispatchComponent;

		@Override
		public void actionPerformed(ActionEvent e) {
			/*
			 * Point p1 = new Point(popup.X, popup.Y);
			 * SwingUtilities.convertPointToScreen(p1, EditableListWithPopup.this);
			 * System.out.println(popup.X + "   " + popup.Y + "     " + p1.x + "   " + p1.y);
			 * try {
			 * Robot robot = new Robot();
			 * robot.mouseMove(p1.x, p1.y);
			 * robot.mousePress(MouseEvent.BUTTON1_MASK);
			 * robot.mouseRelease(MouseEvent.BUTTON1_MASK);
			 * } catch (AWTException e1) {
			 * e1.printStackTrace();
			 * } }
			 */
		}
		/*
		 * public class DeleteActionListener implements ActionListener {
		 * @Override
		 * public void actionPerformed(ActionEvent e) {
		 * Point loc = new Point(popup.X, popup.Y);
		 * int row = locationToIndex(loc);
		 * // IndexedSQL<ViewFilterPreset> currIndexedViewPreset = (IndexedSQL<ViewFilterPreset>)viewPresetList.getValueAt(row);
		 * // GFXSQL.removeViewPreset(currIndexedViewPreset);
		 * // ((DefaultListModel) viewPresetList.getModel()).removeElement(currIndexedViewPreset);
		 * }
		 */
	}

	// --------------------------------------------------------------------
	public static class EditableListModel<T> extends DefaultMutableListModel {
		public boolean	isDirectList;
		private String	extraData	= "";	// use for filenames for stills and footage lists
											// protected LinkedHashSet<T> data = new LinkedHashSet<T>();

		public <T> void create(ArrayList<T> arrayList, ArrayList<User> permittedUsers) {
			// data.clear();
			this.clear();
			for (int i = 0; i < arrayList.size(); i++) {
				T currLine = (T) arrayList.get(i);
				addElement(currLine);
			}
		}

		public void create(Indexed newData, ArrayList<User> permittedUsers) {
			addElement(newData);
		}

		public void addRow(Object newData) {
			this.addElement((T) newData);
		}

		public synchronized String getExtraData() {
			return extraData;
		}

		public synchronized void setExtraData(String extraData) {
			this.extraData = extraData;
		}

		/*
		 * @Override
		 * public boolean isCellEditable(int index) {
		 * return false;
		 * }
		 * @Override
		 * public void setValueAt(Object value, int index) {
		 * if (index >= getSize()) {
		 * addRow(value);
		 * } else {
		 * setElementAt(value, index);
		 * }
		 * }
		 * }
		 */
	}
}
